﻿//--------------------------------------------------------------------------
// 
//  Copyright (c) Chili Software.  All rights reserved. 
// 
//  File: DynamicProperty.cs
//
//  Description: Specifies a dynamic property for a persistent.
// 
//--------------------------------------------------------------------------

using System;
using System.Reflection;
using System.Xml;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using System.Security.Permissions;

namespace Chili.Opf3.DynamicProperties
{
	/// <summary>
	/// Represents a DynamicProperty that contains a value of a simple type.
	/// </summary>
	/// <remarks>
	/// This class is a DynamicProperty that contains a value of a simple type (for example int, string). 
	/// You could also connect a DynamicProperty with a 
	/// <see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see>.
	/// A <see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see> is a set of values,
	/// like an enumeration, that's values can be set as value for the DynamicProperty. You could use such 
	/// a type for option boxes.
	/// </remarks>
	/// <example>
	/// The following example shows how to use the DynamicProperty class in your application.
	/// <code lang="CS">
	/// // ... Other code
	/// 
	/// // Create a DynamicProperty of type string.
	/// DynamicProperty prop1 = new DynamicProperty("PLZ", typeof(string));
	/// prop.Value = "1234";
	/// 
	/// // Create a DynamicProperty of type int.
	/// DynamicProperty prop1 = new DynamicProperty("Age", typeof(int));
	/// prop1.Value = 22;
	/// 
	/// 
	/// DynamicType type = new DynamicType();
	/// type.Values.Add("small");
	/// type.Values.Add("medium");
	/// type.Values.Add("large");
	/// 
	/// // Create a DynamicProperty using a DynamicType.
	/// DynamicProperty prop1 = new DynamicProperty("Size", typeof(string), type);
	/// prop1.Value = "small";
	/// 
	/// // ... Other code
	/// </code>
	/// </example>
	[Serializable]
	public sealed class DynamicProperty : ICloneable
	{
		private DynamicType _dynamicType;
		private Type _type;
		private object _value;
		private string _id;

		/// <summary>
		/// Creates a new instance of the <see cref="DynamicProperty">DynamicProperty Class</see>.
		/// This constructor is required for webservices.
		/// </summary>
		internal DynamicProperty()
		{
		}

		/// <summary>
		/// Creates a new instance of the <see cref="DynamicProperty">DynamicProperty Class</see>.
		/// </summary>
		/// <param name="id">Identifier of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <param name="type">The type of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <exception cref="ArgumentNullException">Id is null.</exception>
		public DynamicProperty(Type type, string id)
		{
			if (id == null)
				throw new ArgumentNullException("id");
			if (type == null)
				throw new ArgumentNullException("type");
			if (!type.IsSubclassOf(typeof(ValueType)) && type != typeof(string))
				throw new ArgumentException("Type must be one of the basic type, like int, string, long, etc. or string");

			this.Type = type;
			_id = id;
		}

		/// <summary>
		/// Creates a new instance of the <see cref="DynamicProperty">DynamicProperty Class</see>.
		/// </summary>
		/// <param name="id">Identifier of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <param name="type">The type of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <param name="value">Value of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <exception cref="ArgumentNullException">Id is null or value is null.</exception>
		public DynamicProperty(Type type, string id, object value) : this(type, id)
		{
			if (value == null)
				throw new ArgumentNullException("value");
			Value = value;
		}

		/// <summary>
		/// Creates a new instance of the <see cref="DynamicProperty">DynamicProperty Class</see>.
		/// </summary>
		/// <param name="id">Identifier of the <see cref="DynamicProperty">DynamicProperty</see>.</param>
		/// <param name="dynamicType"><see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see> 
		/// associated with the dynamic property.</param>
		/// <exception cref="ArgumentNullException">Id is null or dynamicType is null.</exception>
		public DynamicProperty(DynamicType dynamicType, string id)
		{
			if (id == null)
				throw new ArgumentNullException("id");
			if (dynamicType == null)
				throw new ArgumentNullException("dynamicType");

			_id = id;
			this.DynamicType = dynamicType;
		}

		/// <summary>
		/// Specifies the identifier of the <see cref="DynamicProperty">DynamicProperty</see>.
		/// </summary>
		/// <value>A string that represents the identifier of the <see cref="DynamicProperty">DynamicProperty</see>.</value>
		public string Id
		{
			get { return _id; }
			set 
			{
				if (value == null)
					throw new ArgumentNullException("value");
				_id = value; 
			}
		}

		/// <summary>
		/// Specifies the type of the <see cref="DynamicProperty">DynamicProperty</see>.
		/// </summary>
		[XmlIgnore]
		public Type Type
		{
			get { return _type; }
			private set { _type = value; }
		}

		/// <summary>
		/// Specifies the type of the <see cref="DynamicProperty">DynamicProperty</see> as string.
		/// </summary>
		public string TypeAsString
		{
			get { return this.Type.AssemblyQualifiedName; }
			set { this.Type = Type.GetType(value, true); }
		}

		/// <summary>
		/// Specifies the value of the <see cref="DynamicProperty">DynamicProperty</see>.
		/// </summary>
		/// <value>An object that represents the value of the <see cref="DynamicProperty">DynamicProperty</see>.</value>
		public object Value
		{
			get { return _value; }
			set 
			{
				if (_dynamicType != null && (value.GetType() != typeof(string) || !_dynamicType.Values.Contains((string)value)))
					throw new DynamicPropertyException("Dynamic type does not contain value!");
				if (_type != null && value.GetType() != _type)
					throw new DynamicPropertyException("Value is different from dynamic property type!");

				_value = value; 
			}
		}

		/// <summary>
		/// Returns the associated <see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see>. 
		/// If no <see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see> has been set 
		/// returns null.
		/// </summary>
		/// <value>Associated <see cref="Opf3.DynamicProperties.DynamicType">DynamicType</see> or null if
		/// none has been set.</value>
		public DynamicType DynamicType
		{
			get { return _dynamicType; }
			set 
			{ 
				_dynamicType = value;
				// Set the type to string, since it is a dynamic type that is used.
				_type = typeof(string);
			}
		}

		#region ICloneable Members

		/// <summary>
		/// Creates a new object that is a copy of the current instance.
		/// </summary>
		/// <returns>A new object that is a copy of this instance.</returns>
		public object Clone()
		{
			DynamicProperty dynamicProperty  = null;
			if (DynamicType != null)
				dynamicProperty = new DynamicProperty((DynamicType)DynamicType.Clone(), _id);
			else
				dynamicProperty = new DynamicProperty(_type, _id);
			
			dynamicProperty.Value = _value;
			return dynamicProperty;
		}

		#endregion

		/// <summary>
		/// Returns a string representation of the clas.
		/// </summary>
		/// <returns>A string representation of the class.</returns>
		public override string ToString()
		{
			if (_value == null)
				return "";
			return _value.ToString();
		}
	}
}
